﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;

using vJine.Core;
using vJine.Lua.GUI.BLL;

using vJine.Core.Plugin;
using System.Windows.Threading;
using vJine.Core.IoC;
using vJine.Core.Web;
using vJine.Lua.GUI.BML;

namespace vJine.Lua.GUI {
    internal class AppStart : Application {
        public AppStart() {

            this.DispatcherUnhandledException += AppStart_Ex;
        }

        void AppStart_Ex(object sender, DispatcherUnhandledExceptionEventArgs e) {
            e.Handled = true;
            if (e.Exception == null) {
                return;
            } else if (e.Exception.GetType() == typeof(NullReferenceException)) {
                return;
            } else {
                MessageBox.Show(
                    vJine.Core.Log.LogManager.dump("vJine.Lua.GUI", e.Exception), "未知错误"
                    , MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        string root_context = "vCare.AppContext";
        NodeConfig luaConfig = null;
        Dictionary<string, string> Cmds = new Dictionary<string, string>();

        LuaContext luaContext = new LuaContext();

        protected virtual void parse_args() {
            string[] args = Environment.GetCommandLineArgs();
            for (int i = 1, i_max = args.Length - 1; i <= i_max; ) {
                string key = args[i];
                if (key == null || !key.StartsWith("-")) {
                    i += 1; continue;
                }
                string value = args[i + 1];
                if (value == null || value.StartsWith("-")) {
                    i += 1; continue;
                }

                i += 2;
                key = key.ToLower();
                switch (key) {
                    case "-f":
                        this.Cmds.Add("-f", value);
                        break;
                    case "-c":
                        this.Cmds.Add("-c", value);
                        break;
                    case "-t":
                        this.Cmds.Add("-t", value);
                        break;
                    case "-path":
                        this.Cmds.Add("-path", value);
                        break;
                    case "-lua_path":
                        this.Cmds.Add("-lua_path", value);
                        break;
                    case "-lua_cpath":
                        this.Cmds.Add("-lua_cpath", value);
                        break;
                    default:
                        break;
                }
            }

            string Evn_Path = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process);
            if (this.Cmds.ContainsKey("-path")) {
                Environment.SetEnvironmentVariable("PATH", Evn_Path + ";" + this.Cmds["-path"], EnvironmentVariableTarget.Process);
            } else {
                Environment.SetEnvironmentVariable("PATH", Evn_Path + ";.\\lib", EnvironmentVariableTarget.Process);
            }

            if (this.Cmds.ContainsKey("-lua_path")) {
                luaContext.eval(@"package.path = '" + this.Cmds["-lua_path"] + "'");
            } else {
                luaContext.eval(@"package.path = '.\\?.lua;.\\lib\\?.lua'");
            }

            if (this.Cmds.ContainsKey("-lua_cpath")) {
                luaContext.eval(@"package.cpath = '" + this.Cmds["-lua_cpath"] + "'");
            } else {
                luaContext.eval(@"package.cpath = '.\\lib\\?.dll'");
            }
            //
            luaContext.eval(@"require('Class')");
        }

        PluginManager pm = new PluginManager();
        protected virtual void load_context() {
            this.pm.load(@"mod", null, this.luaContext);

            luaContext.reg("use", new Action<string>((string uri) => {
                NodeConfig nConfig = this.luaConfig.get(uri);
                if (nConfig == null) {
                    throw new CoreException("未找到引用节点:{0}", uri);
                }
                luaContext.eval("__THIS__ = this;__SUPER__ = super");
                try {
                    nConfig.init(this.luaContext);
                    this.luaContext.get(nConfig.path);
                } finally {
                    luaContext.eval("this = __THIS__;super = __SUPER__");
                }
            }));

            if (this.Cmds.ContainsKey("-f")) {
                string script_file = this.Cmds["-f"];
                if (!File.Exists(script_file)) {
                    throw new Exception("脚本文件未找到：" + script_file);
                }
                this.luaContext.load(script_file, this.root_context);
                this.luaConfig =
                luaContext.get<NodeConfig>(this.root_context);
                this.luaConfig.set(luaContext, this.root_context);
            } else {
                string main_config_name = "__main";
                this.luaContext.load("../main.lua", main_config_name);
                List<FlowConfig> mc = this.luaContext.get<List<FlowConfig>>(main_config_name);
                this.luaConfig = new NodeConfig() { };
                this.load_context(this.luaConfig, mc, this.root_context);
            }

            luaContext.reg("timestamp", new Action<string>((string format) => {
                string stamp = DateTime.Now.ToString(format);
                luaContext.set(stamp);
            }));
        }

        void load_context(NodeConfig tConfig, List<FlowConfig> mainConfigs, string root) {

            if (this.pm.has_method("main.loader-")) {
                pm.exec("main.loader", this.luaContext, tConfig, mainConfigs, root);
            } else {
                string drl_path = Environment.CurrentDirectory + "/";
                if (mainConfigs == null) {
                    return;
                }

                for (int i = 0, len = mainConfigs.Count; i < len; i++) {
                    FlowConfig mConfig_i = mainConfigs[i];
                    if (mConfig_i == null) {
                        continue;
                    }

                    string node_path = root + "." + (i + 1).ToString();

                    NodeConfig config_i = null;
                    if (!string.IsNullOrEmpty(mConfig_i.type)) {
                        config_i = Class.Create(mConfig_i.type) as NodeConfig;
                        config_i.ui_ignore = false;
                    } else if (!string.IsNullOrEmpty(mConfig_i.path)) {
                        string drl_file = drl_path + mConfig_i.path;
                        if (!File.Exists(drl_file)) {
                            config_i = new NodeConfig();
                            this.load_context(config_i, mConfig_i.nodes, node_path);
                        } else {
                            this.luaContext.load(drl_file, node_path);
                            config_i = this.luaContext.get<NodeConfig>(node_path);
                        }
                        config_i.ui_ignore = true;
                    } else {
                        config_i = new NodeConfig();
                        this.load_context(config_i, mConfig_i.nodes, node_path);
                        config_i.ui_ignore = true;
                    }

                    {
                        config_i.name = mConfig_i.name;
                        config_i.alias = mConfig_i.alias;

                        config_i.tc = mConfig_i.tc;
                    }
                    config_i.set(this.luaContext, node_path);

                    tConfig.Add(config_i);
                }
            }

            set_dispatcher(tConfig);
        }

        static void set_dispatcher(NodeConfig nConfig) {
            nConfig.guiDispatcer = System.Windows.Threading.Dispatcher.CurrentDispatcher;
            for (int i = 0; i < nConfig.Count; i++) {
                set_dispatcher(nConfig[i]);
            }
        }

        void load_testcase(NodeConfig nConfig) {
            if (!string.IsNullOrEmpty(nConfig.tc)) {
                string name = nConfig.name; string alias = nConfig.alias;
                TestCatalog.load_case(nConfig, this.luaConfig, true);
                nConfig.name = name; nConfig.alias = alias; nConfig.is_batch = true;
            }

            foreach (NodeConfig nConfig_i in nConfig) {
                this.load_testcase(nConfig_i);
            }
        }

        ConfigManager configManager = new ConfigManager();
        public virtual void load_config() {
            if (this.Cmds.ContainsKey("-c")) {
                this.configManager.set(this.luaContext, this.luaConfig, this.Cmds["-c"]);
            } else {
                this.configManager.set(this.luaContext, this.luaConfig);
            }
            this.configManager.setConfig(this.luaConfig);
        }

        protected virtual void load_gui() {
            Window winMain = null;
            if (this.Cmds.ContainsKey("-f")) {
                winMain = new WinDebug().set_bound(this.luaContext, this.luaConfig);
            } else {
                winMain = new WinFlow().set_bound(this.luaContext, this.luaConfig, this.configManager);
            }

            if (this.Cmds.ContainsKey("-t")) {
                winMain.Title = this.Cmds["-t"];
            }

            this.luaContext.reg("gui.title", new Action<String>((string title) => {
                winMain.Dispatcher.Invoke(new Action(() => {
                    winMain.Title = title;
                    winMain.UpdateLayout();
                }));
            }));

            this.luaContext.reg("gui.alert", new Exec<string>((string message) => {
                winMain.Dispatcher.Invoke(new Action(()=>{
                    MessageBox.Show(winMain, message, "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
                    winMain.UpdateLayout();
                }));
                
            }));
            this.luaContext.reg("gui.notify", new Exec<string>((string message) => {
                winMain.Dispatcher.Invoke(new Action(()=>{
                    MessageBox.Show(winMain, message, "提醒", MessageBoxButton.OK, MessageBoxImage.Information);
                    winMain.UpdateLayout();
                }));
                
            }));
            this.luaContext.reg("gui.confirm", new Call<string,string>((string message) => {
                string rMsg = "CANCEL";
                winMain.Dispatcher.Invoke(new Action(()=>{
                    MessageBoxResult R = MessageBox.Show(winMain, message, "确认", MessageBoxButton.OKCancel, MessageBoxImage.Question);
                    if (R == MessageBoxResult.OK) {
                        rMsg = "OK";
                    }
                    winMain.UpdateLayout();
                }));
                return rMsg;
            }));
            this.luaContext.reg("gui.debug", new Exec<string>((string message) => {
                winMain.Dispatcher.BeginInvoke(new Action(() => {
                    WinDebug winD = new WinDebug().set_bound(this.luaContext, this.luaConfig);
                    winD.ShowDialog();
                })).Wait();
            }));
            //
            this.luaContext.reg("utils.float_to_bytes",
                new Call<byte[], float>((float v) => {
                    byte[] data = BitConverter.GetBytes(v);
                    Array.Reverse(data);
                    return data;
                }));
            this.luaContext.reg("utils.bytes_to_float",
                new Call<float, byte[]>((byte[] v) => {
                    Array.Reverse(v);
                    return BitConverter.ToSingle(v, 0);
                }));

            this.Run(winMain);
        }

        void load_ext_methods() {
            this.luaContext.reg("io.dir.exist", new Call<bool,string>((string dir_name) => {
                return Directory.Exists(dir_name);
            }));
            this.luaContext.reg("io.dir.create", new Exec<string>((string dir_name) => {
                Directory.CreateDirectory(dir_name);
            }));
            this.luaContext.reg("io.dir.remove", new Exec<string>((string dir_name) => {
                Directory.Delete(dir_name, true);
            }));
            this.luaContext.reg("io.file.exist", new Call<bool, string>((string file_name) => {
                return File.Exists(file_name);
            }));
            this.luaContext.reg("io.file.write", new Exec<string, string>((string file_name, string contents) => {
                File.WriteAllText(file_name, contents);
            }));
            this.luaContext.reg("io.file.append", new Action<string, string, string>((string file_name, string contents, string headers) => {
                if (!string.IsNullOrEmpty(headers) && !File.Exists(file_name)) {
                    File.WriteAllText(file_name, headers);
                }
                File.AppendAllText(file_name, contents);
            }));

        }

        public void load() {
            this.parse_args();

            this.load_context();

            this.load_config();

            this.load_ext_methods();

            if (this.luaConfig.Count > 0) {
                NodeConfig root = new NodeConfig();
                foreach(NodeConfig nSub in this.luaConfig){
                    root.Add(nSub);
                    nSub.Parent = root;
                }
                this.luaConfig[0].IsActive = true;
            }

            this.load_testcase(this.luaConfig);

            this.load_gui();
        }
    }
}
